<?php
// a encoder to encode the scode, it is a reversible algorithm.
namespace app\scodetrace\encoder;
use \Hashids\Hashids;

class ScodeEncoder {
	const base='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
	const prefix_length=3;
	private $encoder;
	private static $_instance;

	public static function getInstance() {
		if (ScodeEncoder::$_instance) {
			return ScodeEncoder::$_instance;
		} else {
			return (ScodeEncoder::$_instance=new ScodeEncoder());
		}
	}

	private function getEncoder($company_id) {
		$cache = cache('SCODEENCODER_'.$company_id);
		if ($cache) {
			return $cache;
		}
		$company = model('baseinfo/Company')->get($company_id);
		if (!$company)
			return null;
		$data = [
			'prefix' => 'FW',
			'length' => '12',
		];
		$baseStr = ScodeEncoder::base;
		if ($company->socde_character && strlen($company->socde_character)>16 ) {
			$baseStr = $company->socde_character;
		}
		if ($company->scode_prefix) {
			$data['prefix'] = $company->scode_prefix;
		}
		if ($company->scode_length && $company->scode_length >= 8) {
			$data['length'] = $company->scode_length;
		}
		$data['encoder'] = new Hashids('xlfwsy_source',$data['length']-strlen($data['prefix']),$baseStr);
		cache('SCODEENCODER_'.$company_id,$data,config('cache_param.scode_encorder'),'SCODEENCODER');
		return $data;
	}

	/**
	 * encrypt a set of id to scodes,you should specific the which company own these ids.
	 *
	 * @param  Array|int $ids        the id set to be encrypted
	 * @param  int $company_id the owner of the ids
	 * @return Array|int             will return an array,the order same as the ids(the count also.)
	 */
	public function encode($ids,$company_id) {
		$encoder = $this->getEncoder($company_id);
		if (!$encoder) {
			return null;
		}
		if (is_array($ids)) {
			$ids2= [];
			$c = count($ids);
			for ($i=0;$i<$c;$i++) {
				$ids2[$i] = $encoder['prefix'].$encoder['encoder']->encode([$ids[$i],8141]);
			}
			$ids=$ids2;
		} else {
			$ids = $encoder['prefix'].$encoder['encoder']->encode([$ids,8141]);
		}
		return $ids;
	}

	/**
	 * decrypt the cryptograph to id,which is xl_scode primary key
	 * @param  String $cryptograph the scode.
	 * @return int              the scode id.
	 */
	public function decode($cryptograph) {
		if (!$cryptograph)
			return null;
		$encoder = $this->getEncoder($this->getInfoByscode($cryptograph));
		if (!$encoder) {
			return null;
		}
		return $this->_decode(substr(($cryptograph),ScodeEncoder::prefix_length), $encoder);
	}

	private function getInfoByscode($scode) {
		$company_id=db('xl_company')->where([['scode_length','=',strlen($scode)],['scode_prefix','=',substr($scode, 0, ScodeEncoder::prefix_length)]])->value('id');
		return $company_id;
	}

	private function _decode($cryptograph, $encoder) {
		$tmp = $encoder['encoder']->decode($cryptograph);
		if (count($tmp) != 2 || $tmp[1] != 8141) {
			return null;
		}
		return $tmp[0];
	}
}





?>